<?php

@require_once "./config.php";

if (function_exists('date_default_timezone_set')) {
	@date_default_timezone_set('Etc/GMT-8');
}

// log file format: filename,time,ip\n
define("DOWNLOAD_LOG_FILE", $BASEDIR . "/downloads.log");

// 缓存目录, 需要给 777 权限
define("CACHE_DIR", $BASEDIR . "/cache/");

if (isset($_GET['dl'])) {
    action_download_file($_GET['dl']);
    exit();
}

// 回退用的版本号, 正常情况下可不更新
$HASH_BASE_FALLBACK = array(
    'v3.4' => 'v3.4-20210119',
    'v3.5' => 'master'
);

// Todo: 新版本正式发布后, 需要修改此处接入发行版探测. 老版本停服时, 需要修改此处取消发行版探测.
$HASH_BASE = array(
    'v3.4' => get_last_release('v3.4', constant('CACHE_DIR'), $HASH_BASE_FALLBACK),
    'v3.5' => 'master'
);

$files = all_zip_files($BASEDIR);

// 基于日期与时间，倒序排列
// 文件名格式：DZX-编码-版本-构建时间-hash值
usort($files, function ($a, $b) {
    preg_match('/DZX-.*?-.*?-(\d{12})-(.*?).zip/', $a, $ma);
    preg_match('/DZX-.*?-.*?-(\d{12})-(.*?).zip/', $b, $mb);
    return strcmp($mb[1], $ma[1]);
});

$files = group_files_by_date($files);

class DZRelease
{
	public $branch;
	public $year;
	public $month;
	public $day;
	public $hash;
	public $filename;
	public $downloads;

	public function __construct($branch, $year, $month, $day, $hash, $filename, $downloads = 0)
	{
		$this->branch = $branch;
		$this->year = $year;
		$this->month = $month;
		$this->day = $day;
		$this->hash = $hash;
		$this->filename = $filename;
		$this->downloads = isset($downloads) ? $downloads : 0;
	}

	public function group_name()
	{
		return $this->year . '年' . $this->month . '月';
	}

	public function date()
	{
		return sprintf("%s-%s-%s", $this->year, $this->month, $this->day);
	}
}

function all_zip_files($dir)
{
	$files = array();
	if ($handle = opendir($dir)) {
		while (false !== ($entry = readdir($handle))) {
			if (substr($entry, -4) === ".zip") {
				$files[] = $entry;
			}
		}
		closedir($handle);
	}
	return $files;
}

/*
 * 输入文件列表，返回一个按日期、hash组织的DZRelease对象列表
 */
function group_files_by_date($files)
{
	$rs = array();
	$downloads = read_download_log_file();
	foreach ($files as $file) {
		if (preg_match('/DZX-.*?-(.*?)-(\d{4})(\d{2})(\d{2})\d*?-(.*?).zip/', $file, $m)) {
			$dz = new DZRelease($m[1], $m[2], $m[3], $m[4], $m[5], $file, $downloads[$file]);
			$rs[$dz->group_name()][$dz->hash][] = $dz;
		}
	}
	return $rs;
}

// 获取相同版本下的上一个hash，用于比较
function get_prev_hash($files, $release_index, $build_index, $date_index)
{
	global $HASH_BASE;
	$date_keys = array_keys($files);
	$build_keys = array_keys($files[$date_keys[$date_index]]);
	$current_release = $files[$date_keys[$date_index]][$build_keys[$build_index]][$release_index];

	$need_next = false;
	foreach ($files as $date => $builds) {
		foreach ($builds as $hash => $releases) {
			foreach ($releases as $index => $release) {
				if ($current_release->filename === $release->filename) { // 找到当前文件，那下一个同版本的release，就是要比较的
					$need_next = true;
					break;
				}
				if ($need_next && $current_release->branch === $release->branch) {
					return $release->hash;
				}
			}
		}
	}
	return $HASH_BASE[$current_release->branch];
}

// 读取日志文件，统计每个文件的下载次数
function read_download_log_file() {
	$result = array();
	if (!file_exists(DOWNLOAD_LOG_FILE)) return $result;
	$lines = file_get_contents(DOWNLOAD_LOG_FILE);
	foreach (explode("\n", $lines) as $line) {
		list($filename, $time, $ip) = explode(",", $line);
		$result[$filename] += 1;
	}
	return $result;
}

function write_download_log($file) {
	$logline = $file . "," . date('Y-m-d H:i:s') . "," . $_SERVER["REMOTE_ADDR"] . "\n";
	file_put_contents(DOWNLOAD_LOG_FILE, $logline, FILE_APPEND);
}

function action_download_file($file) {
	global $BASEURL;
	write_download_log($file);
	header('Location: ' . $BASEURL . $file);
}

// 获取最新 Release
function get_last_release($branch, $cachedir, $fallback) {
    // 缓存文件
    $cachename = $cachedir . 'release-' . $branch . '.txt';
    // 如果文件存在且非空, 则判断文件是否是最新, 如果是最新则直接显示, 否则删除缓存文件
    if (file_exists($cachename) && is_readable($cachename)) {
        if (filemtime($cachename) < time() - 86400) {
            if (is_writable($cachename)) {
                unlink($cachename);
            }
        } else {
            return file_get_contents($cachename);
        }
    }
    // 如果无法创建缓存文件, 直接走 fallback, 避免反复请求导致 IP 地址被封禁
    if (!touch($cachename)) {
        return $fallback[$branch];
    }
    // 如果文件权限无问题但不是最新, 则获取最新的 Release
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
    curl_setopt($ch, CURLOPT_HEADER, false);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
    curl_setopt($ch, CURLOPT_URL, "https://gitee.com/Discuz/DiscuzX/releases");
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
    $contents = curl_exec($ch);
    curl_close($ch);

    if (preg_match('/class="title" href="\/Discuz\/DiscuzX\/releases\/'.$branch.'(.*)" target="_blank"/m', $contents, $m)) {
        $release = $branch.$m[1];
    }
    if (!isset($release) && preg_match('/href="\/Discuz\/DiscuzX\/releases\/'.$branch.'(.*)" target="_blank"/m', $contents, $m)) {
        $release = $branch.$m[1];
    }

    // 如果无法获取正确版本, 直接走 fallback, 并且 fallback 也需要缓存, 避免反复请求导致 IP 地址被封禁
    $release = isset($release) ? $release : $fallback[$branch];
    if (is_writable($cachename)) {
        file_put_contents($cachename, $release, LOCK_EX);
    }

    return $release;
}

?>

<!doctype html>
<html lang="zh-cn">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <title>DiscuzX 3.x Daily Build Download Site</title>
    <!-- Bootstrap core CSS -->
    <link href="https://cdn.bootcss.com/twitter-bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet">
    <link href="https://at.alicdn.com/t/font_1561081_l1mwhq3tpn9.css" rel="stylesheet">
    <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
    <script src="https://cdn.bootcss.com/twitter-bootstrap/4.3.1/js/bootstrap.min.js"></script>
    <style>
        html {
            font-size: 14px;
        }

        @media (min-width: 768px) {
            html {
                font-size: 14px;
            }
        }

        .container {
            max-width: 960px;
            padding-top: 66px;
        }

        .navbar .in .nav li {
            display: block;
            float: none;
            width: 100%;
        }

        .hidden {
            display: none;
        }
    </style>
</head>

<body>
    <nav class="navbar fixed-top navbar-expand-sm navbar-dark bg-dark">
        <a class="navbar-brand" href="#"><img src="https://www.discuz.net/favicon.ico" height="32"/> Discuz! 每日构建 </a>
        <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
            <span class="navbar-toggler-icon"></span>
        </button>
        <div class="collapse navbar-collapse" id="navbarNav">
            <ul class="navbar-nav">
                <li class="nav-item active branch-sel-li">
                    <a class="nav-link branch-sel-a" href="#">所有版本</a>
                </li>
                <li class="nav-item branch-sel-li">
                    <a class="nav-link branch-sel-a" href="#">3.5</a>
                </li>
                <li class="nav-item branch-sel-li">
                    <a class="nav-link branch-sel-a" href="#">3.4</a>
                </li>
            </ul>
        </div>
    </nav>

    <div class="container">
        <div class="alert alert-warning" role="alert">
            本页面提供的是正在开发中的代码打包，仅限于测试使用，请不要用于生产系统。 <a href="https://gitee.com/Discuz/DiscuzX/releases" target="_blank">Discuz! X 产品正式版下载</a>
        </div>
        <div class="alert alert-success" role="alert">
            <h4 class="alert-heading">说明</h4>
            <p>如果某一天没有打包文件，说明这一天没有新的代码提交。</p>
            <p>点击提交ID前的<span class="iconfont icon-diff"></span>可查看本次构建与上次构建之间的提交历史与文件差异；点击<span class="iconfont icon-diff2"></span>可查看本次构建与上个大版本之间的提交历史与文件差异。</p>
        </div>
        <table class="table table-bordered">
            <thead class="thead-dark">
                <tr>
                    <th scope="col" class="text-center">日期</th>
                    <th scope="col" class="text-center">版本</th>
                    <th scope="col" class="text-center">最后提交ID</th>
                    <th scope="col" class="text-center">文件名称</th>
                    <th scope="col" class="text-center">下载</th>
                </tr>
            </thead>
            <tbody>
                <?php
                $date_count = count($files);
                for ($date_index = 0; $date_index < $date_count; $date_index++) { // files : date => builds
                    $keys = array_keys($files);
                    $date = $keys[$date_index];
                    $builds = $files[$date];
                    ?>
                    <tr class="table-secondary">
                        <td colspan="5" class="text-center"><?= $date; ?></td>
                    </tr>
                    <?php
                        $build_count = count($builds);
                        for ($build_index = 0; $build_index < $build_count; $build_index++) { // builds: hash => releases
                            $keys = array_keys($builds);
                            $hash = $keys[$build_index];
                            $releases = $builds[$hash];
                            sort($releases);
                            foreach ($releases as $index => $release) {
                    ?>
                            <tr class="branch-<?= str_replace(".", "", $release->branch); ?>">
                                <?php if ($index == 0) {
                                    $prev_hash = get_prev_hash($files, $index, $build_index, $date_index);
                                ?>
                                    <th class="align-middle" rowspan="<?= count($releases); ?>"><?= $release->date(); ?></th>
                                    <th class="align-middle" rowspan="<?= count($releases); ?>"><?= $release->branch; ?></th>
                                    <th class="align-middle" rowspan="<?= count($releases); ?>">
                                        <a class="iconfont icon-diff" target="_blank" href="https://gitee.com/Discuz/DiscuzX/compare/<?= $prev_hash; ?>...<?= $hash; ?>"></a>
                                        <a class="iconfont icon-diff2" target="_blank" href="https://gitee.com/Discuz/DiscuzX/compare/<?= $HASH_BASE[$release->branch]; ?>...<?= $hash; ?>"></a>
                                        <?= $hash; ?>
                                    </th>
                                <?php } ?>
                                <td class="align-middle"><?= $release->filename; ?></td>
                                <td class="align-middle text-center"><a href="<?= "?dl=" . $release->filename; ?>" class="iconfont icon-download"></a> <?= $release->downloads ?></td>
                            </tr>
                        <?php } ?>
                    <?php
                            $prev_hash = $hash;
                        } ?>
                <?php } ?>
            </tbody>
        </table>
        <footer class="pt-4 my-md-5 pt-md-5 border-top">
            <div class="row">
                <div class="col-12 col-md text-center text-danger">
                    <h6>本页面提供的是正在开发中的代码打包，仅限于测试使用，请不要用于生产系统。</h6>
                </div>
            </div>
            <div class="row">
                <div class="col-12 col-md text-center">
                    <p>Copyright &copy; 2001-<?php echo date('Y'); ?> Tencent Cloud.</p>
                </div>
            </div>
        </footer>
    </div>
    <script language="JavaScript">
        $(".branch-sel-a").on('click', function (e) {
            $(".branch-sel-li").removeClass('active');
            $(e.target.parentElement).addClass('active');
            $("tr").removeClass('hidden');
            if (e.target.innerText !== "所有版本") {
                $('[class^="branch-"]').not(".branch-v" + e.target.innerText.replace(".", "")).addClass('hidden');
            }
        })
    </script>
</body>

</html>

